import {
  FastifyInstance,
  FastifyPluginAsync,
  FastifyReply,
  FastifyRequest,
} from "fastify";
import svgCaptcha from "svg-captcha";
import crypto from "crypto";

// 类型定义
declare module "fastify" {
  interface FastifyInstance {
    captchaStore: Map<string, CaptchaData>;
    ipLimitStore: Map<string, RateLimitData>;
  }
}

interface CaptchaData {
  text: string;
  expireAt: number;
  clientIp: string;
  userAgent: string;
}

interface RateLimitData {
  count: number;
  resetAt: number;
}

interface VerifyRequestBody {
  captchaId: string;
  captchaText: string;
}

// 清理过期验证码的定时任务
function setupCaptchaCleanup(fastify: FastifyInstance) {
  const interval = setInterval(
    () => {
      const now = Date.now();
      for (const [id, data] of fastify.captchaStore.entries()) {
        if (data.expireAt < now) {
          fastify.captchaStore.delete(id);
        }
      }
    },
    5 * 60 * 1000,
  ); // 每5分钟清理一次

  fastify.addHook("onClose", () => clearInterval(interval));
}

const captchaRoutes: FastifyPluginAsync = async (fastify, opts) => {
  // 初始化存储
  fastify.decorate("ipLimitStore", new Map<string, RateLimitData>());

  // 设置定时清理任务
  setupCaptchaCleanup(fastify);

  // IP限流中间件
  const ipRateLimiter = async (
    request: FastifyRequest,
    reply: FastifyReply,
  ) => {
    const clientIp =
      request.ip ||
      (request.headers["x-forwarded-for"] as string)?.split(",")[0]?.trim() ||
      "unknown";
    const now = Date.now();

    let ipRecord = fastify.ipLimitStore.get(clientIp);

    // 重置周期为1分钟
    if (!ipRecord || ipRecord.resetAt < now) {
      // todo 暂时10分钟
      ipRecord = { count: 0, resetAt: now + 60 * 1000 * 10 };
      fastify.ipLimitStore.set(clientIp, ipRecord);
    }

    // 增加计数并检查限制
    ipRecord.count++;

    // 设置响应头
    reply.header("X-RateLimit-Limit", "10");
    reply.header("X-RateLimit-Remaining", Math.max(0, 10 - ipRecord.count));
    reply.header("X-RateLimit-Reset", Math.ceil(ipRecord.resetAt / 1000));

    if (ipRecord.count > 10) {
      const retryAfterSeconds = Math.ceil((ipRecord.resetAt - now) / 1000);
      reply.code(429).send({
        success: false,
        error: `请求过于频繁，请在 ${retryAfterSeconds} 秒后再试`,
        retryAfter: retryAfterSeconds,
      });
      throw new Error("Rate limit exceeded");
    }
  };

  // 生成验证码
  fastify.get(
    "/generate-captcha",
    {
      preHandler: ipRateLimiter,
      schema: {
        response: {
          200: {
            type: "object",
            properties: {
              success: { type: "boolean" },
              data: {
                type: "object",
                properties: {
                  captchaId: { type: "string" },
                  svg: { type: "string" },
                },
                required: ["captchaId", "svg"],
              },
            },
            required: ["success", "data"],
          },
          429: {
            type: "object",
            properties: {
              success: { type: "boolean" },
              error: { type: "string" },
              retryAfter: { type: "number" },
            },
          },
        },
      },
    },
    async (request, reply) => {
      const clientIp =
        request.ip ||
        (request.headers["x-forwarded-for"] as string)?.split(",")[0]?.trim() ||
        "unknown";
      const userAgent = request.headers["user-agent"] || "unknown";

      // 生成验证码
      const captcha = svgCaptcha.create({
        size: 4,
        ignoreChars: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", // 忽略所有字母
        color: true,
        noise: 3,
        width: 120,
        height: 40,
        fontSize: 50,
        charPreset: "0123456789", // 只使用数字
      });

      // 生成安全的随机ID
      const captchaId = crypto.randomBytes(16).toString("hex");

      // 存储验证码
      fastify.captchaStore.set(captchaId, {
        text: captcha.text.toLowerCase(),
        expireAt: Date.now() + 5 * 60 * 1000, // 5分钟过期
        clientIp,
        userAgent,
      });

      return {
        success: true,
        data: {
          captchaId,
          svg: captcha.data,
        },
      };
    },
  );

  // 验证验证码
  fastify.post(
    "/verify",
    {
      preHandler: ipRateLimiter,
      schema: {
        body: {
          type: "object",
          required: ["captchaId", "captchaText"],
          properties: {
            captchaId: { type: "string" },
            captchaText: { type: "string" },
          },
          additionalProperties: false,
        },
        response: {
          200: {
            type: "object",
            properties: {
              success: { type: "boolean" },
              message: { type: "string" },
            },
          },
          400: {
            type: "object",
            properties: {
              success: { type: "boolean" },
              error: { type: "string" },
            },
          },
          429: {
            type: "object",
            properties: {
              success: { type: "boolean" },
              error: { type: "string" },
              retryAfter: { type: "number" },
            },
          },
        },
      },
    },
    async (request, reply) => {
      const { captchaId, captchaText } = request.body as VerifyRequestBody;
      const clientIp =
        request.ip ||
        (request.headers["x-forwarded-for"] as string)?.split(",")[0]?.trim() ||
        "unknown";

      // 验证输入
      if (!captchaId || !captchaText) {
        return reply.code(400).send({
          success: false,
          error: "验证码ID和验证码文本不能为空",
        });
      }

      // 获取存储的验证码
      const storedCaptcha = fastify.captchaStore.get(captchaId);

      // 检查验证码是否存在和过期
      if (!storedCaptcha || storedCaptcha.expireAt < Date.now()) {
        fastify.captchaStore.delete(captchaId);
        return reply.code(400).send({
          success: false,
          error: "验证码已过期或不存在",
        });
      }

      // 检查客户端IP
      if (storedCaptcha.clientIp !== clientIp) {
        fastify.captchaStore.delete(captchaId);
        return reply.code(400).send({
          success: false,
          error: "验证码无效，请重新获取",
        });
      }

      // 验证码匹配
      const isValid = storedCaptcha.text === captchaText.toLowerCase();
      fastify.captchaStore.delete(captchaId);

      if (isValid) {
        return {
          success: true,
          message: "验证码验证成功",
        };
      }
      return reply.code(400).send({
        success: false,
        error: "验证码错误",
      });
    },
  );
};

export default captchaRoutes;
